﻿Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

Namespace System.Workflow.ComponentModel.Design
    Friend Class GlyphManager
        Inherits WorkflowDesignerMessageFilter
        Implements IDesignerGlyphProviderService
        ' Methods
        Friend Sub New()
        End Sub

        Protected Overrides Sub Dispose(ByVal disposing As Boolean)
            Me.designerGlyphProviders.Clear
            Me.activeGlyph = Nothing
            Me.activeDesigner = Nothing
            Dim service As IServiceContainer = [TryCast](MyBase.GetService(GetType(IServiceContainer)),IServiceContainer)
            If ((Not service Is Nothing) AndAlso (Not MyBase.GetService(GetType(IDesignerGlyphProviderService)) Is Nothing)) Then
                service.RemoveService(GetType(IDesignerGlyphProviderService))
            End If
            MyBase.Dispose(disposing)
        End Sub

        Friend Sub DrawDesignerGlyphs(ByVal e As ActivityDesignerPaintEventArgs, ByVal designer As ActivityDesigner)
            Dim glyph As DesignerGlyph
            For Each glyph In Me.GetDesignerGlyphs(designer)
                glyph.Draw(e.Graphics, designer)
            Next
            If ((Not Me.activeGlyph Is Nothing) AndAlso (designer Is Me.activeDesigner)) Then
                Me.activeGlyph.DrawActivated(e.Graphics, Me.activeDesigner)
            End If
        End Sub

        Private Function GetActivityDesigners(ByVal logicalViewPort As Rectangle) As ActivityDesigner()
            Dim list As New List([Of] ActivityDesigner)
            Dim isEmpty As Boolean = logicalViewPort.IsEmpty
            Dim safeRootDesigner As ActivityDesigner = ActivityDesigner.GetSafeRootDesigner(MyBase.ParentView)
            If (Not safeRootDesigner Is Nothing) Then
                Dim stack As New Stack([Of] Object)
                stack.Push(safeRootDesigner)
                Dim designer2 As CompositeActivityDesigner = [TryCast](safeRootDesigner,CompositeActivityDesigner)
                If ((Not designer2 Is Nothing) AndAlso (designer2.ContainedDesigners.Count > 0)) Then
                    stack.Push(designer2.ContainedDesigners)
                End If
                Do While (stack.Count > 0)
                    Dim obj2 As Object = stack.Pop
                    Dim is2 As ICollection = [TryCast](obj2,ICollection)
                    If (Not is2 Is Nothing) Then
                        Dim designer3 As ActivityDesigner
                        For Each designer3 In is2
                            If ((isEmpty OrElse logicalViewPort.IntersectsWith(designer3.Bounds)) AndAlso designer3.IsVisible) Then
                                stack.Push(designer3)
                                designer2 = [TryCast](designer3,CompositeActivityDesigner)
                                If ((Not designer2 Is Nothing) AndAlso (designer2.ContainedDesigners.Count > 0)) Then
                                    stack.Push(designer2.ContainedDesigners)
                                End If
                            End If
                        Next
                    Else
                        list.Add(DirectCast(obj2, ActivityDesigner))
                    End If
                Loop
            End If
            Return list.ToArray
        End Function

        Friend Function GetDesignerGlyphs(ByVal designer As ActivityDesigner) As ActivityDesignerGlyphCollection
            Dim glyphs As New ActivityDesignerGlyphCollection
            If (Not designer.Glyphs Is Nothing) Then
                glyphs.AddRange(designer.Glyphs)
            End If
            Dim provider As IDesignerGlyphProvider
            For Each provider In Me.designerGlyphProviders
                Dim collection As ActivityDesignerGlyphCollection = provider.GetGlyphs(designer)
                If (Not collection Is Nothing) Then
                    glyphs.AddRange(collection)
                End If
            Next
            glyphs.Sort(New Comparison([Of] DesignerGlyph)(AddressOf DesignerGlyph.OnComparePriority))
            Return glyphs
        End Function

        Private Function GlyphFromPoint(ByVal point As Point, <Out> ByRef activityDesigner As ActivityDesigner) As DesignerGlyph
            activityDesigner = Nothing
            Dim parentView As WorkflowView = MyBase.ParentView
            If (Not parentView Is Nothing) Then
                Dim rectangles As New RectangleCollection
                Dim designer As ActivityDesigner
                For Each designer In Me.GetActivityDesigners(parentView.ClientRectangleToLogical(New Rectangle(Point.Empty, parentView.ViewPortSize)))
                    If Not rectangles.IsPointInsideAnyRectangle(point) Then
                        Dim glyph As DesignerGlyph
                        For Each glyph In Me.GetDesignerGlyphs(designer)
                            If (glyph.GetBounds(designer, False).Contains(point) AndAlso glyph.CanBeActivated) Then
                                activityDesigner = designer
                                Return glyph
                            End If
                        Next
                    End If
                    rectangles.AddRectangle(designer.Bounds)
                Next
            End If
            Return Nothing
        End Function

        Protected Overrides Sub Initialize(ByVal parentView As WorkflowView)
            MyBase.Initialize(parentView)
            Dim service As IServiceContainer = [TryCast](MyBase.GetService(GetType(IServiceContainer)),IServiceContainer)
            If (Not service Is Nothing) Then
                If (Not MyBase.GetService(GetType(IDesignerGlyphProviderService)) Is Nothing) Then
                    service.RemoveService(GetType(IDesignerGlyphProviderService))
                End If
                service.AddService(GetType(IDesignerGlyphProviderService), Me)
            End If
        End Sub

        Protected Overrides Function OnMouseDoubleClick(ByVal eventArgs As MouseEventArgs) As Boolean
            If (Not Me.activeGlyph Is Nothing) Then
                Me.activeGlyph.Activate(Me.activeDesigner)
                Return True
            End If
            Return False
        End Function

        Protected Overrides Function OnMouseDown(ByVal eventArgs As MouseEventArgs) As Boolean
            If (Not Me.activeGlyph Is Nothing) Then
                Me.activeGlyph.Activate(Me.activeDesigner)
                Return True
            End If
            Return False
        End Function

        Protected Overrides Function OnMouseEnter(ByVal eventArgs As MouseEventArgs) As Boolean
            Me.RefreshActiveGlyph(MyBase.ParentView.ClientPointToLogical(New Point(eventArgs.X, eventArgs.Y)))
            Return False
        End Function

        Protected Overrides Function OnMouseHover(ByVal eventArgs As MouseEventArgs) As Boolean
            Me.RefreshActiveGlyph(MyBase.ParentView.ClientPointToLogical(New Point(eventArgs.X, eventArgs.Y)))
            Return False
        End Function

        Protected Overrides Function OnMouseMove(ByVal eventArgs As MouseEventArgs) As Boolean
            Me.RefreshActiveGlyph(MyBase.ParentView.ClientPointToLogical(New Point(eventArgs.X, eventArgs.Y)))
            Return False
        End Function

        Private Sub RefreshActiveGlyph(ByVal point As Point)
            Dim parentView As WorkflowView = MyBase.ParentView
            If (Not parentView Is Nothing) Then
                Dim activeGlyph As DesignerGlyph = Me.activeGlyph
                If ((Me.activeGlyph Is Nothing) OrElse Not Me.activeGlyph.GetBounds(Me.activeDesigner, True).Contains(point)) Then
                    Dim activityDesigner As ActivityDesigner = Nothing
                    Dim glyph2 As DesignerGlyph = Me.GlyphFromPoint(point, activityDesigner)
                    If (Not Me.activeGlyph Is Nothing) Then
                        parentView.InvalidateLogicalRectangle(Me.activeGlyph.GetBounds(Me.activeDesigner, True))
                    End If
                    Me.activeGlyph = glyph2
                    Me.activeDesigner = activityDesigner
                    If (Not Me.activeGlyph Is Nothing) Then
                        parentView.InvalidateLogicalRectangle(Me.activeGlyph.GetBounds(Me.activeDesigner, True))
                    End If
                End If
                If (Not activeGlyph Is Me.activeGlyph) Then
                    If ((Not Me.activeGlyph Is Nothing) AndAlso Me.activeGlyph.CanBeActivated) Then
                        parentView.Cursor = Cursors.Hand
                    ElseIf (parentView.Cursor Is Cursors.Hand) Then
                        parentView.Cursor = Cursors.Default
                    End If
                End If
            End If
        End Sub

        Private Sub System.Workflow.ComponentModel.Design.IDesignerGlyphProviderService.AddGlyphProvider(ByVal glyphProvider As IDesignerGlyphProvider) Implements IDesignerGlyphProviderService.AddGlyphProvider
            If Not Me.designerGlyphProviders.Contains(glyphProvider) Then
                Me.designerGlyphProviders.Add(glyphProvider)
                MyBase.ParentView.InvalidateClientRectangle(Rectangle.Empty)
            End If
        End Sub

        Private Sub System.Workflow.ComponentModel.Design.IDesignerGlyphProviderService.RemoveGlyphProvider(ByVal glyphProvider As IDesignerGlyphProvider) Implements IDesignerGlyphProviderService.RemoveGlyphProvider
            Me.designerGlyphProviders.Remove(glyphProvider)
            MyBase.ParentView.InvalidateClientRectangle(Rectangle.Empty)
        End Sub


        ' Properties
        Private ReadOnly Property System.Workflow.ComponentModel.Design.IDesignerGlyphProviderService.GlyphProviders As ReadOnlyCollection([Of] IDesignerGlyphProvider)
            Get
                Return Me.designerGlyphProviders.AsReadOnly
            End Get
        End Property


        ' Fields
        Private activeDesigner As ActivityDesigner
        Private activeGlyph As DesignerGlyph
        Private designerGlyphProviders As List([Of] IDesignerGlyphProvider) = New List([Of] IDesignerGlyphProvider)

        ' Nested Types
        Private Class RectangleCollection
            ' Methods
            Friend Sub AddRectangle(ByVal rectangle As Rectangle)
                Me.rectangles.Add(rectangle)
            End Sub

            Friend Function IsPointInsideAnyRectangle(ByVal p As Point) As Boolean
                Dim i As Integer
                For i = 0 To Me.rectangles.Count - 1
                    Dim rectangle As Rectangle = Me.rectangles.Item(i)
                    If rectangle.Contains(p) Then
                        Return True
                    End If
                Next i
                Return False
            End Function


            ' Fields
            Private rectangles As List([Of] Rectangle) = New List([Of] Rectangle)
        End Class
    End Class
End Namespace

